import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTPClient;

public class FtpOperator {

	FtpConfig ftpConfig;

	public FtpOperator(FtpConfig ftpConfig) {

		this.ftpConfig = ftpConfig;

	}

	public FtpOperator(String serverHost, String port, String user,
			String password) {

		this(new FtpConfig(serverHost, port, user, password, null, null));

	}

	public FtpOperator(String serverHost, String port) {

		this(new FtpConfig(serverHost, port, "anonymous", "", null, null));

	}

	public FtpConfig getFtpConfig() {

		return ftpConfig;

	}

	public void setFtpConfig(FtpConfig ftpConfig) {

		this.ftpConfig = ftpConfig;

	}

	public FTPClient startClient() throws FtpException {

		FTPClient ftpClient = new FTPClient();

		String serverHost = ftpConfig.getServerHost();

		String serverPort = ftpConfig.getServerPort();

		int port = 0;

		try {

			port = Integer.parseInt(serverPort);

		} catch (Exception e) {

			throw new FtpException("Wrong FTP port:"
					+ ftpConfig.getServerPort());

		}

		try {

			ftpClient.connect(serverHost, port);

			String username = ftpConfig.getUser(),

			password = ftpConfig.getPassword();

			if (!ftpClient.login(username, password))

				throw new FtpException("Wrong FTP login/passwd:" + username
						+ "/" + password);

		} catch (SocketException se) {

			throw new FtpException("Socket timeout cannot set.");

		} catch (IOException ioe) {

			throw new FtpException("Cannot open ftp connection: " + serverHost
					+ ":" + serverPort);

		}

		return ftpClient;

	}

	public void disposeClient(FTPClient ftpClient) {

		if (ftpClient == null)

			return;

		if (ftpClient.isConnected()) {

			try {

				ftpClient.disconnect();

			} catch (IOException e) {

			}

		}

	}

	public void retrieveRemoteFile(FTPClient ftpClient,
			String remoteRelativePathName, File localFile) throws FtpException {

		try {

			remoteRelativePathName = remoteRelativePathName.trim();

			String filename = null;

			String workingDir = null;

			String serverBaseDir = ftpConfig.getRemoteBaseDir();

			if (serverBaseDir == null)

				serverBaseDir = ".";

			int lastSlash = remoteRelativePathName.lastIndexOf("/");

			if (lastSlash == remoteRelativePathName.length() - 1)

				throw new FtpException("Cannot fetch a file with empty name.");

			if (lastSlash > 0) {

				filename = remoteRelativePathName.substring(lastSlash + 1);

				workingDir = serverBaseDir + "/"
						+ remoteRelativePathName.substring(0, lastSlash);

			} else {

				filename = remoteRelativePathName;

				workingDir = serverBaseDir;

			}

			if (!ftpClient.changeWorkingDirectory(workingDir)) {

				throw new FtpException("Cannot change to dir:" + workingDir);

			}

			File localDirFile = localFile.getParentFile();

			localDirFile.mkdirs();

			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

			FileOutputStream localOutputStream = null;

			try {

				localOutputStream = new FileOutputStream(localFile);

				if (!ftpClient.retrieveFile(filename, localOutputStream))

					throw new FtpException("Cannot get file:" + filename
							+ " in dir:" + workingDir);

			} catch (IOException e) {

				throw new FtpException("File IO error.");

			} finally {

				if (localOutputStream != null) {

					try {

						localOutputStream.close();

					} catch (IOException e) {

					}

				}

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error.");

		}

	}

	public void retrieveRemoteFile(String remoteRelativePathName, File localFile)
			throws FtpException {

		FTPClient ftpClient = startClient();

		try {

			retrieveRemoteFile(ftpClient, remoteRelativePathName, localFile);

		} finally {

			disposeClient(ftpClient);

		}

	}

	public File retrieveRemoteFile(FTPClient ftpClient,
			String remoteRelativePathName, String localRelativePathName)
			throws FtpException {

		File retrievedFile = null;

		try {

			remoteRelativePathName = remoteRelativePathName.trim();

			String filename = null;

			String workingDir = null;

			String serverBaseDir = ftpConfig.getRemoteBaseDir();

			if (serverBaseDir == null)

				serverBaseDir = ".";

			String localBaseDir = ftpConfig.getLocalBaseDir();

			if (localBaseDir == null)

				localBaseDir = ".";

			int lastSlash = remoteRelativePathName.lastIndexOf("/");

			if (lastSlash == remoteRelativePathName.length() - 1)

				throw new FtpException("Cannot fetch a file with empty name.");

			if (lastSlash > 0) {

				filename = remoteRelativePathName.substring(lastSlash + 1);

				workingDir = serverBaseDir + "/"
						+ remoteRelativePathName.substring(0, lastSlash);

			} else {

				filename = remoteRelativePathName;

				workingDir = serverBaseDir;

			}

			retrievedFile = new File(localBaseDir + "/" + localRelativePathName);

			if (!ftpClient.changeWorkingDirectory(workingDir)) {

				throw new FtpException("Cannot change to dir:" + workingDir);

			}

			File localDirFile = retrievedFile.getParentFile();

			localDirFile.mkdirs();

			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

			FileOutputStream localOutputStream = null;

			try {

				localOutputStream = new FileOutputStream(retrievedFile);

				if (!ftpClient.retrieveFile(filename, localOutputStream))

					throw new FtpException("Cannot get file:" + filename
							+ " in dir:" + workingDir);

			} catch (IOException e) {

				throw new FtpException("File IO error.");

			} finally {

				if (localOutputStream != null) {

					try {

						localOutputStream.close();

					} catch (IOException e) {

					}

				}

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error.");

		}

		return retrievedFile;

	}

	public File retrieveRemoteFile(String remoteRelativePathName,
			String localRelativePathName) throws FtpException {

		FTPClient ftpClient = startClient();

		File retrievedFile = null;

		try {

			retrievedFile = retrieveRemoteFile(ftpClient,
					remoteRelativePathName, localRelativePathName);

		} finally {

			disposeClient(ftpClient);

		}

		return retrievedFile;

	}

	public File retrieveRemoteFile(FTPClient ftpClient, String relativePathName)
			throws FtpException {

		File retrievedFile = null;

		try {

			relativePathName = relativePathName.trim();

			String filename = null;

			String workingDir = null;

			String localDir = null;

			String serverBaseDir = ftpConfig.getRemoteBaseDir();

			if (serverBaseDir == null)

				serverBaseDir = ".";

			String localBaseDir = ftpConfig.getLocalBaseDir();

			if (localBaseDir == null)

				localBaseDir = ".";

			int lastSlash = relativePathName.lastIndexOf("/");

			if (lastSlash == relativePathName.length() - 1)

				throw new FtpException("Cannot fetch a file with empty name.");

			if (lastSlash > 0) {

				filename = relativePathName.substring(lastSlash + 1);

				workingDir = serverBaseDir + "/"
						+ relativePathName.substring(0, lastSlash);

				localDir = localBaseDir + "/"
						+ relativePathName.substring(0, lastSlash);

			} else {

				filename = relativePathName;

				workingDir = serverBaseDir;

				localDir = localBaseDir;

			}

			if (!ftpClient.changeWorkingDirectory(workingDir)) {

				throw new FtpException("Cannot change to dir:" + workingDir);

			}

			File localDirFile = new File(localDir);

			localDirFile.mkdirs();

			retrievedFile = new File(localDirFile, filename);

			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

			FileOutputStream localOutputStream = null;

			try {

				localOutputStream = new FileOutputStream(retrievedFile);

				if (!ftpClient.retrieveFile(filename, localOutputStream))

					throw new FtpException("Cannot get file:" + filename
							+ " in dir:" + workingDir);

			} catch (IOException e) {

				throw new FtpException("File IO error.");

			} finally {

				if (localOutputStream != null) {

					try {

						localOutputStream.close();

					} catch (IOException e) {

					}

				}

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error.");

		}

		return retrievedFile;

	}

	public File retrieveRemoteFile(String relativePathName) throws FtpException {

		FTPClient ftpClient = startClient();

		File retrievedFile = null;

		try {

			retrievedFile = retrieveRemoteFile(ftpClient, relativePathName);

		} finally {

			disposeClient(ftpClient);

		}

		return retrievedFile;

	}

	public void putRemoteFile(FTPClient ftpClient, File localFile,
			String remoteDir, String newName) throws FtpException {

		if (!localFile.isFile())

			throw new FtpException(
					"Upload target doesn't exist or is not a file.");

		String serverBaseDir = ftpConfig.getRemoteBaseDir();

		if (serverBaseDir == null)

			serverBaseDir = ".";

		if (newName == null)

			newName = localFile.getName();

		try {

			String putDir = null;

			if (remoteDir == null) {

				putDir = serverBaseDir;

			} else {

				putDir = serverBaseDir + "/" + remoteDir;

			}

			ftpClient.makeDirectory(putDir);

			if (!ftpClient.changeWorkingDirectory(putDir)) {

				throw new FtpException("Cannot change to dir:" + putDir);

			}

			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

			FileInputStream inputStream = null;

			try {

				inputStream = new FileInputStream(localFile);

				if (!ftpClient.storeFile(newName, inputStream)) {

					throw new FtpException("Upload file error.");

				}

			} catch (IOException e) {

				throw new FtpException("Local file read error.");

			} finally {

				if (inputStream != null) {

					try {

						inputStream.close();

					} catch (IOException e) {

					}

				}

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error.");

		}

	}

	public void putRemoteFile(File localFile, String remoteDir, String newName)
			throws FtpException {

		if (!localFile.isFile())

			throw new FtpException(
					"Upload target doesn't exist or is not a file.");

		FTPClient ftpClient = startClient();

		try {

			putRemoteFile(ftpClient, localFile, remoteDir, newName);

		} finally {

			disposeClient(ftpClient);

		}

	}

	public void renameRemoteFile(FTPClient ftpClient, String remoteDir,
			String oldFileName, String newFileName)

	throws FtpException {

		String serverBaseDir = ftpConfig.getRemoteBaseDir();

		if (serverBaseDir == null)

			serverBaseDir = ".";

		String workingDir = null;

		if (remoteDir == null) {

			workingDir = serverBaseDir;

		} else {

			workingDir = serverBaseDir + "/" + remoteDir;

		}

		try {

			if (!ftpClient.changeWorkingDirectory(workingDir)) {

				throw new FtpException("Cannot change to dir:" + workingDir);

			}

			if (!ftpClient.rename(oldFileName, newFileName)) {

				throw new FtpException("Cannot rename'" + oldFileName
						+ "' to '" + newFileName + "'");

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error");

		}

	}

	public void renameRemoteFile(String remoteDir, String oldFileName,
			String newFileName)

	throws FtpException {

		FTPClient ftpClient = startClient();

		try {

			renameRemoteFile(ftpClient, remoteDir, oldFileName, newFileName);

		} finally {

			disposeClient(ftpClient);

		}

	}

	public void deleteRemoteFile(FTPClient ftpClient, String relativePathName)
			throws FtpException {

		if (relativePathName == null)

			throw new FtpException("File to delete is null");

		String serverBaseDir = ftpConfig.getRemoteBaseDir();

		if (serverBaseDir == null)

			serverBaseDir = ".";

		try {

			if (!ftpClient.deleteFile(serverBaseDir + "/" + relativePathName)) {

				throw new FtpException(
						"Delete file error, or file does not exist.");

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error");

		}

	}

	public void deleteRemoteFile(String relativePathName) throws FtpException {

		if (relativePathName == null)

			throw new FtpException("File to delete is null");

		FTPClient ftpClient = startClient();

		try {

			deleteRemoteFile(ftpClient, relativePathName);

		} finally {

			disposeClient(ftpClient);

		}

	}

	/**
	 * 
	 * Check file existence by using ftp command "RNFR" (rename from)
	 * 
	 * Since no RNTO follows it, there will be no side effect.
	 * 
	 * @param relativePathName
	 * 
	 * @return if remote file/dir exist, return true, otherwise, return false.
	 * 
	 * @throws FtpException
	 */

	public boolean checkRemoteFileExistence(FTPClient ftpClient,
			String relativePathName) throws FtpException {

		if (relativePathName == null) {

			return false;

		}

		String serverBaseDir = ftpConfig.getRemoteBaseDir();

		if (serverBaseDir == null)

			serverBaseDir = ".";

		try {

			int code = ftpClient.rnfr(serverBaseDir + "/" + relativePathName);

			if (code == 550)

				return false;

			return true;

		} catch (IOException e) {

			throw new FtpException("FTP operation error.");

		}

	}

	/**
	 * 
	 * Check file existence by using ftp command "RNFR" (rename from)
	 * 
	 * Since no RNTO follows it, there will be no side effect.
	 * 
	 * @param relativePathName
	 * 
	 * @return if remote file/dir exist, return true, otherwise, return false.
	 * 
	 * @throws FtpException
	 */

	public boolean checkRemoteFileExistence(String relativePathName)
			throws FtpException {

		if (relativePathName == null) {

			return false;

		}

		FTPClient ftpClient = startClient();

		boolean ret = false;

		try {

			ret = checkRemoteFileExistence(ftpClient, relativePathName);

		} finally {

			disposeClient(ftpClient);

		}

		return ret;

	}

	public String[] listRemoteDir(FTPClient ftpClient, String relativePathName)
			throws FtpException {

		String serverBaseDir = ftpConfig.getRemoteBaseDir();

		if (serverBaseDir == null)

			serverBaseDir = ".";

		String[] ret = new String[0];

		try {

			String[] remoteRet = ftpClient.listNames(serverBaseDir
					+ (relativePathName != null ? "/" + relativePathName : ""));

			if (remoteRet == null)
				return ret;

			else if (remoteRet.length >= 0)

			{

				ret = getFileNamesWithoutPath(remoteRet);

			}

		} catch (IOException e) {

			throw new FtpException("FTP operation error.");

		}

		return ret;

	}

	public String[] listRemoteDir(String relativePathName) throws FtpException {

		String[] ret = new String[0];

		FTPClient ftpClient = startClient();

		try {

			String[] remoteRet = listRemoteDir(ftpClient, relativePathName);

			if (remoteRet == null)
				return ret;

			else if (remoteRet.length >= 0)

			{

				ret = getFileNamesWithoutPath(remoteRet);

			}

		} finally {

			disposeClient(ftpClient);

		}

		return ret;

	}

	public String[] getFileNamesWithoutPath(String[] oFileNames)

	{

		String[] ret = new String[0];

		try {

			if (oFileNames == null)
				return ret;

			else if (oFileNames.length >= 0)

			{

				ret = new String[oFileNames.length];

				for (int i = 0; i < oFileNames.length; i++)

				{

					// to get only fileName,not include path

					ret[i] = getSingleFileNameWithoutPath(oFileNames[i]);

				}

			}

		}

		catch (Exception e)

		{

			e.printStackTrace();

		}

		return ret;

	}

	public String getSingleFileNameWithoutPath(String oFileName) {
		String nFileName = "";
		if (oFileName == null)
			return nFileName;
		int markPostion = oFileName.lastIndexOf("/");
		if (markPostion >= 0) {
			nFileName = oFileName.substring(markPostion + 1);
		} else {
			nFileName = oFileName;
		}
		return nFileName;
	}

	public static void main(String[] args) throws FtpException {
		FtpOperator oper = new FtpOperator("127.0.0.1", "21");
		/*String[] list = oper.listRemoteDir("./FavoriteVideo/d5857d3c3b26ca2d67c9f9bc67207399[0].pp");
		for (String string : list) {
			System.out.println(string);
		}*/
		boolean flag = oper.checkRemoteFileExistence("1.txt");
		System.out.println(flag);
	}
}
